using System;
using System.Collections.Generic;
using System.IO;
using Baula.TextGen.ActiveTemplating.Tokenizer.TokenParsers;
using Baula.TextGen.ActiveTemplating.Tokenizer.Tokens;

namespace Baula.TextGen.ActiveTemplating.Tokenizer
{
    public class TemplateTokenizer
    {
        private readonly string _template;
        private TextReader _reader;
        private int _pos;

        public TemplateTokenizer(string template)
        {
            _template = template;
        }

        // TODO: Refactor
        public IEnumerable<IToken> Tokenize()
        {
            if (!string.IsNullOrEmpty(_template))
            {
                _reader = new StringReader(_template);
                ITokenParser currentParser = null;
                _pos = 0;
                ReadNextCharacter();
                while (!ReachedEndOfStream())
                {
                    if (currentParser == null)
                    {
                        currentParser = CreateTokenParser();
                    }
                    else
                    {
                        if (currentParser.CanAppend(CurrentChar))
                        {
                            currentParser.Append(CurrentChar);
                            ReadNextCharacter();
                        }
                        else
                        {
                            yield return currentParser.CreateToken();
                            currentParser = CreateTokenParser();
                        }
                    }
                }
                if (currentParser != null)
                {
                    yield return currentParser.CreateToken();
                }
            }
            yield return new EofToken();
        }

        private void ReadNextCharacter()
        {
            if (!NextChar.HasValue)
            {
                CurrentChar = (char) _reader.Read();
                if (!ReachedEndOfStream())
                    NextChar = (char) _reader.Read();
            }
            else
            {
                CurrentChar = NextChar.Value;
                if (!ReachedEndOfStream())
                    NextChar = (char) _reader.Read();
                else
                    NextChar = null;
            }
            _pos++;
        }

        private char? NextChar { get; set; }

        private char CurrentChar { get; set; }

        public string CurrentString
        {
            get 
            {
                if (ReachedEndOfStream())
                    return string.Empty;
                if (NextChar.HasValue)
                    return string.Format("{0}{1}", CurrentChar, NextChar.Value);
                return CurrentChar.ToString();
            }
        }

        private bool ReachedEndOfStream()
        {
            return CurrentChar == UInt16.MaxValue;
        }

        private ITokenParser CreateTokenParser()
        {
            var startColumn = _pos;
            ITokenParser currentTokenParser;
            if (BlockExpressionParser.CanStartToken(CurrentString))
            {
                currentTokenParser = new BlockExpressionParser();
                ReadNextCharacter();
                ReadNextCharacter();
            }
            else
            if (InlineExpressionParser.CanStartToken(CurrentChar))
            {
                currentTokenParser = new InlineExpressionParser();
                ReadNextCharacter();
            }
            else
            {
                currentTokenParser = new LiteralParser();
                currentTokenParser.Append(CurrentChar);
                ReadNextCharacter();
            }
            currentTokenParser.StartColumn = startColumn;
            return currentTokenParser;
        }
    }
}